package io.xiyou.account1.service.impl;

import io.xiyou.account1.AccountInfoService;
import io.xiyou.account1.dao.mapper.AccountFreezeMapper;
import io.xiyou.account1.dao.mapper.AccountInfoMapper;
import io.xiyou.account1.dao.model.AccountInfo;
import io.xiyou.account1.enums.AccountTypeEnum;
import io.xiyou.account1.service.support.HmilySupport;
import io.xiyou.account2.Account2Service;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.config.annotation.Service;
import org.dromara.hmily.annotation.Hmily;
import org.dromara.hmily.core.concurrent.threadlocal.HmilyTransactionContextLocal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * @ClassName：AccountInfoServiceImpl
 * @Author：西柚
 * @Date：2021/9/30 11:30 下午
 * @Versiion：1.0
 */
@Slf4j
@Service
public class AccountInfoServiceImpl implements AccountInfoService {

    @Autowired
    private AccountInfoMapper accountInfoMapper;
    @Autowired
    private AccountFreezeMapper accountFreezeMapper;
    @Autowired
    private HmilySupport hmilySupport;

    @Reference
    private Account2Service account2Service;

    /**
     * 账户扣款
     * try幂等校验
     * try悬挂处理
     * 检查余额是够扣减金额
     * 扣减金额
     *
     * @param accountId
     * @param money
     */
    @Override
    @Transactional
    //只要标记@Hmily就是try方法，在注解中指定confirm、cancel两个方法的名字
    @Hmily(confirmMethod = "commit", cancelMethod = "rollback")
    public void subtractAccountBalance(Long accountId, BigDecimal money) {
        //获取全局事务id
        String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        log.info("account1 try begin 开始执行...xid:{}", transId);
        //幂等判断 判断local_try_log表中是否有try日志记录，如果有则不再执行
        if (hmilySupport.isExistTry(transId) > 0) {
            log.info("account1 try 已经执行，无需重复执行,xid:{}", transId);
            return;
        }

        //try悬挂处理，如果cancel、confirm有一个已经执行了，try不再执行
        if (hmilySupport.isExistConfirm(transId) > 0 || hmilySupport.isExistCancel(transId) > 0) {
            log.info("account1 try悬挂处理  cancel或confirm已经执行，不允许执行try,xid:{}", transId);
            return;
        }

        // 查询账户信息
        AccountInfo accountInfo = accountInfoMapper.selectById(accountId);
        if (Objects.isNull(accountInfo)) {
            throw new RuntimeException("account1 try 账户不存在,xid:{}" + transId);
        }

        //扣减金额
        if (accountInfoMapper.subtractAccountBalance(accountId, money) <= 0) {
            //扣减失败
            throw new RuntimeException("account1 try 扣减金额失败,xid:{}" + transId);
        }

        // 增加冻结金额
        accountFreezeMapper.addFreezeMoney(accountId, accountInfo.getAccountType(), money);

        //插入try执行记录,用于幂等判断
        hmilySupport.addTry(transId);

        Long transferAccountId;
        BigDecimal transferMoney = BigDecimal.ZERO;
        if (AccountTypeEnum.CNY.getCode().equals(accountInfo.getAccountType())) {
            transferAccountId = 4L;
            transferMoney = money.divide(new BigDecimal(7));
        } else {
            transferAccountId = 3L;
            transferMoney = money.multiply(new BigDecimal(7));
        }

        //远程调用李四，转账
        if (!account2Service.addAccountBalance(transferAccountId, transferMoney)) {
            throw new RuntimeException("bank1 远程调用李四微服务失败,xid:{}" + transId);
        }
        if (money.intValue() == 2) {
            throw new RuntimeException("人为制造异常,xid:{}" + transId);
        }
        log.info("account1 try end 结束执行...xid:{}", transId);
    }

    /**
     * confirm方法
     *
     * @param accountId
     * @param money
     */
    @Transactional
    public void commit(Long accountId, BigDecimal money) {
        // 查询账户信息
        AccountInfo accountInfo = accountInfoMapper.selectById(accountId);

        // 转账成功、扣除冻结金额
        accountFreezeMapper.subtractFreezeMoney(accountId, accountInfo.getAccountType(), money);

        //获取全局事务id
        String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        log.info("account1 confirm begin 开始执行...xid:{},accountId:{},money:{}", transId, accountId, money);
    }


    /**
     * cancel方法
     * cancel幂等校验
     * cancel空回滚处理
     * 增加可用余额
     *
     * @param accountId
     * @param money
     */
    @Transactional
    public void rollback(Long accountId, BigDecimal money) {
        //获取全局事务id
        String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
        log.info("account1 cancel begin 开始执行...xid:{}", transId);
        //	cancel幂等校验
        if (hmilySupport.isExistCancel(transId) > 0) {
            log.info("account1 cancel 已经执行，无需重复执行,xid:{}", transId);
            return;
        }
        //cancel空回滚处理，如果try没有执行，cancel不允许执行
        if (hmilySupport.isExistTry(transId) <= 0) {
            log.info("account1 空回滚处理，try没有执行，不允许cancel执行,xid:{}", transId);
            return;
        }
        // 查询账户信息
        AccountInfo accountInfo = accountInfoMapper.selectById(accountId);

        // 增加可用余额
        accountInfoMapper.addAccountBalance(accountId, money);
        // 减少冻结金额
        accountFreezeMapper.subtractFreezeMoney(accountId, accountInfo.getAccountType(), money);

        //插入一条cancel的执行记录
        hmilySupport.addCancel(transId);
        log.info("account1 cancel end 结束执行...xid:{}", transId);

    }

}
